﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.VersionControl.Common;
using System.IO;
using DifferenceEngine;
using System.Collections;

namespace PowerShell.Host.PsTFS.CdmLet.PsObject
{
    public class PsDiff
    {

        static List<Ps2FilesDiff> retour = new List<Ps2FilesDiff>();
        static Workspace workspace;

        public static List<Ps2FilesDiff> Diff(string source, string target,  TeamFoundationServer tfs, VersionControlServer vcs,bool Information)
        {
            
            return Diff(source, target, tfs,VersionSpec.Latest,VersionSpec.Latest, vcs,Information);

        }
        public static List<Ps2FilesDiff> Diff(string source, string target, TeamFoundationServer tfs, int ChangesetL, int ChangesetR, VersionControlServer vcs, bool Information)
        {
            if (workspace == null)
            {
                // Creation du workspace. 
                workspace = vcs.CreateWorkspace(Guid.NewGuid().ToString(), vcs.AuthenticatedUser);
            }
            // Parse the 
            VersionSpec version1 = ParseVersionSpec(source, workspace);
            VersionSpec version2 = ParseVersionSpec(target, workspace);

            return Diff(source, target, tfs, version1, version2, vcs, Information);

        }
        public static List<Ps2FilesDiff> Diff(string source, string target, TeamFoundationServer tfs, VersionSpec vsSource, VersionSpec vsTarget, VersionControlServer vcs,bool Information)
        {

            retour.Clear();

            if (workspace == null)
            {
                // Creation du workspace. 
                workspace = vcs.CreateWorkspace(Guid.NewGuid().ToString(), vcs.AuthenticatedUser);
            }

            try
            {
                DisplayVersionDiff(source, target, vsSource, vsTarget,Information);
            }
            catch (TeamFoundationServerException e)
            {
                // If something goes wrong, such as not having access to the server, display
                // the appropriate error message.
                Console.Error.WriteLine(e.Message);
                PsLog.AddLog(null,e.ToString());
                Environment.Exit(1);
            }
            if (workspace != null)
            {
                workspace.Delete();
                workspace = null;
            }
            return retour;
        }
        // Fill in the workspace name if it is null.
        static VersionSpec ParseVersionSpec(String spec, Workspace workspace)
        {
            String user = workspace.VersionControlServer.TeamFoundationServer.AuthenticatedUserName;

            VersionSpec version = VersionSpec.ParseSingleSpec(spec, user);

            // If the user happened to specify only W for the workspace spec, we'll have to
            // fill in the workspace here (the parse method doesn't know it).
            WorkspaceVersionSpec wvs = version as WorkspaceVersionSpec;
            if (wvs != null && wvs.Name == null)
            {
                wvs.Name = workspace.Name;
            }

            return version;
        }

        
        // This approach compares two different versions of the tree.  When version1 is W
        // (the workspace version spec) and version2 is T (tip/latest), the results will be
        // be equivalent to the other approach (we get some different properties).
        static void DisplayVersionDiff(string source, string target, VersionSpec version1, VersionSpec version2,bool Information)
        {
            // We need the list of items at the specified versions.  This call only gets
            // information about the versions that have been checked in and does not include
            // any pending changes.  As a result, it does not return pending adds, branches,
            // and undeletes.
            ItemSet itemSet1 = workspace.VersionControlServer.GetItems(source, version1, RecursionType.Full);
            ItemSet itemSet2 = workspace.VersionControlServer.GetItems(target, version2, RecursionType.Full);
            Item[] items1 = itemSet1.Items;
            Item[] items2 = itemSet2.Items;

            // Build hash tables of the items so that we can quickly determine which items
            // are common.  Every item in the repository is assigned a unique item ID.
            // The item ID never changes, even though the item's path (item.ServerItem) may
            // change due to being renamed or moved.
            List<PsFileDiff> itemHash1 = CreateList(items1,source);
            List<PsFileDiff> itemHash2 = CreateList(items2,target);

            foreach (PsFileDiff dfd in itemHash1)
            {
                if (dfd.Name != null)
                {
                    string extension = Path.GetExtension(dfd.Name);


                    var query = from s in itemHash2
                                where s.Name == dfd.Name
                                where s.Folder == dfd.Folder
                                select s;
                    if (query.Count() > 0)
                    {
                        if (IsExtensionValide(extension))
                        {
                            string file1 = dfd.Download();
                            string file2 = query.First().Download();
                            retour.Add(Comprare(dfd, query.First(), Information));
                            query.First().Dispose();
                        }
                    }
                    else
                    {
                        Ps2FilesDiff ps = new Ps2FilesDiff();
                        ps.NameL = dfd;
                        ps.TDiff = TypeDiff.Left;
                        ps.Folder = dfd.Folder;
                        retour.Add(ps);
                    }

                }
            }
            foreach (PsFileDiff dfd in itemHash2)
            {
                if (dfd.Name != null)
                {
                    var query = from s in itemHash1
                                where s.Name == dfd.Name
                                where s.Folder == dfd.Folder
                                select s;
                    if (query.Count() == 0)
                    {
                        Ps2FilesDiff ps = new Ps2FilesDiff();
                        ps.NameL = dfd;
                        ps.TDiff = TypeDiff.Right;
                        ps.Folder = dfd.Folder;
                        retour.Add(ps);
                    }
                }
            }
        }

        private static bool IsExtensionValide(string extension)
        {
            List<String> extensionNoValide = new List<string>();
            extensionNoValide.Add(".ppt");
            extensionNoValide.Add(".pptx");
            extensionNoValide.Add(".doc");
            extensionNoValide.Add(".docx");
            extensionNoValide.Add(".xsl");
            extensionNoValide.Add(".xslx");
            extensionNoValide.Add(".bmp");
            extensionNoValide.Add(".jpg");
            extensionNoValide.Add(".png");
            extensionNoValide.Add(".gif");


            var e = from ex in extensionNoValide
                    where ex == extension
                    select ex;
            if (e.Count() > 0)
            {
                return false;
            }
            else
            {
                return true;
            }

            throw new NotImplementedException();
        }

        // Creates a hash table of the files and folders.
        static List<PsFileDiff> CreateList(Item[] items,string source)
        {
            List<PsFileDiff> itemHash = new List<PsFileDiff>();
            foreach (Item item in items)
            {
                PsFileDiff pfd = new PsFileDiff();
                pfd.FolderSource = source;
                pfd.Item = item;

                itemHash.Add(pfd);
            }

            return itemHash;
        }

        static Ps2FilesDiff Comprare(PsFileDiff source, PsFileDiff target,bool Information)
        {
            Ps2FilesDiff pfd = new Ps2FilesDiff();
            pfd.NameL = source;
            pfd.NameR = target;
            pfd.Folder = source.Folder;
            pfd.ResultDiff = new List<DiffResultSpan>();
            pfd.TDiff = TypeDiff.Identical;

            DiffList_TextFile sLF = null;
            DiffList_TextFile dLF = null;


            sLF = new DiffList_TextFile(source.LocalFile);
            dLF = new DiffList_TextFile(target.LocalFile);


            try
            {
                double time = 0;
                DiffEngine de = new DiffEngine();
                time = de.ProcessDiff(sLF, dLF, DiffEngineLevel.SlowPerfect);

                ArrayList rep = de.DiffReport();
                
                for (int i = 0; i < rep.Count; i++)
                {
                    DiffResultSpan drs = rep[i] as DiffResultSpan;
                    pfd.ResultDiff.Add(drs);
                    
                }

                var t = from v in pfd.ResultDiff
                        where v.Status != DiffResultSpanStatus.NoChange
                        select v;
                if (t.Count() > 0)
                {
                    pfd.TDiff = TypeDiff.Diff;
                }

                if (Information)
                {
                    pfd.Results(sLF, dLF, rep, time);
                }
               
            }
            catch (Exception ex)
            {
                PsLog.AddLog(null, ex.ToString());

            }
            return pfd;
        }

    }
}
